package ubadb.core.components.bufferManager.bufferPool;

import java.util.HashMap;
import java.util.Map;

import ubadb.core.common.Page;
import ubadb.core.common.PageId;
import ubadb.core.common.TableId;
import ubadb.core.components.bufferManager.bufferPool.BufferFrame;
import ubadb.core.components.bufferManager.bufferPool.BufferPool;
import ubadb.core.exceptions.BufferPoolException;

/**
 * Multiple buffers, each dedicated to a specific table, with it's own strategy and size.
 * 
 * @author Grupo6
 */
public class PerTableBufferPool implements BufferPool {

	private final Map<TableId, BufferPool> poolsMap = new HashMap<TableId, BufferPool>();

	public PerTableBufferPool() {
	}

	public void addNewBufferPoolForTable(TableId tableId, BufferPool pool) throws BufferPoolException {
		if (poolsMap.containsKey(tableId)) {
			throw new BufferPoolException("A buffer for table " + tableId + " already exists in the pool");
		} else {
			poolsMap.put(tableId, pool);
		}
	}

	public void dropBufferPoolForTable(TableId tableId) throws BufferPoolException {
		if (poolsMap.containsKey(tableId)) {
			poolsMap.remove(tableId);
		} else {
			throw new BufferPoolException("No buffer for table " + tableId);
		}
	}

	public BufferPool getBufferPoolForPage(PageId page) {
		return poolsMap.get(page.getTableId());
	}

	@Override
	public boolean isPageInPool(PageId pageId) {
		BufferPool pool = getBufferPoolForPage(pageId);
		return pool != null && pool.isPageInPool(pageId);
	}

	@Override
	public BufferFrame getBufferFrame(PageId pageId) throws BufferPoolException {
		if (isPageInPool(pageId)) {
			return getBufferPoolForPage(pageId).getBufferFrame(pageId);
		} else {
			throw new BufferPoolException("The requested page is not in the pool");
		}
	}

	@Override
	public boolean hasSpace(PageId pageToAddId) {
		BufferPool pool = getBufferPoolForPage(pageToAddId);
		return pool != null && pool.hasSpace(pageToAddId);
	}

	@Override
	public BufferFrame addNewPage(Page page) throws BufferPoolException {
		PageId pageId = page.getPageId();
		if (!hasSpace(pageId)) {
			throw new BufferPoolException("No space in pool for new page");
		} else if (isPageInPool(pageId)) {
			throw new BufferPoolException("Page already exists in the pool");
		} else {
			return getBufferPoolForPage(pageId).addNewPage(page);
		}

	}

	@Override
	public void removePage(PageId pageId) throws BufferPoolException {
		if (isPageInPool(pageId)) {
			getBufferPoolForPage(pageId).removePage(pageId);
		} else {
			throw new BufferPoolException("Cannot remove an unexisting page");
		}
	}

	@Override
	public BufferFrame findVictim(PageId pageIdToBeAdded) throws BufferPoolException {
		BufferPool pool = getBufferPoolForPage(pageIdToBeAdded);
		if (pool == null) {
			throw new BufferPoolException("Cannot find a victim page for removal: no buffer allocated for table "
					+ pageIdToBeAdded.getTableId());
		}
		try {
			return pool.findVictim(pageIdToBeAdded);
		} catch (BufferPoolException e) {
			throw new BufferPoolException("Cannot find a victim page for removal", e);
		}
	}

	@Override
	public int countPagesInPool() {
		int pageCount = 0;
		for (BufferPool pool : poolsMap.values())
			pageCount += pool.countPagesInPool();
		return pageCount;
	}
}
